/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 * Description: 音频播放模块 - 通过I2S+DMA LLI播放解码后的音频数据到扬声器
 */

#include "little_test.h"
#include "i2s.h"
#include "dma.h"
#include "osal_debug.h"
#include "osal_task.h"
#include "osal_semaphore.h"
#include "securec.h"
#include <stdlib.h>
#include <string.h>

// DMA LLI节点结构
typedef struct {
    dma_lli_transfer_config_t config;
    struct dma_lli_transfer_config_t *next;
} dma_lli_node_t;

/**
 * @brief DMA传输完成回调函数
 * @param channel DMA通道号
 * @param transfer_stat 传输状态
 * @param user_data 用户数据
 */
static void audio_playback_dma_callback(uint8_t channel, dma_transfer_stat_t transfer_stat, uintptr_t user_data)
{
    audio_playback_t *playback = (audio_playback_t *)user_data;
    
    if (playback == NULL || !playback->initialized) {
        return;
    }

    if (transfer_stat == DMA_TRANSFER_STAT_SUCC) {
        // DMA传输成功，更新缓冲区索引
        playback->current_buffer_index = (playback->current_buffer_index + 1) % LITTLE_TEST_BUFFER_COUNT;
        playback->frames_played++;
        
        // 释放信号量通知可以填充新数据
        osal_sem_up(&playback->buffer_ready_sem);
    } else {
        // DMA传输错误
        playback->error_count++;
        osal_printk("[AUDIO_PLAYBACK] DMA transfer error: %d\n", transfer_stat);
    }
}

/**
 * @brief 初始化I2S配置（播放）
 * @param playback 音频播放结构体指针
 * @return 0成功，其他失败
 */
static int audio_playback_init_i2s(audio_playback_t *playback)
{
    i2s_config_t i2s_config = {0};
    
    // 配置I2S参数（播放使用不同的I2S总线或配置为全双工）
    i2s_config.drive_mode = I2S_MASTER_MODE;  // 主机模式
    i2s_config.transfer_mode = I2S_STD_MODE;  // 标准模式
    i2s_config.data_width = I2S_DATA_WIDTH_16;  // 16位数据
    i2s_config.channels_num = I2S_CHANNELS_2;   // 双声道
    i2s_config.sampling_frequency = LITTLE_TEST_SAMPLE_RATE;  // 采样率16kHz
    i2s_config.clk_edge = I2S_CLK_EDGE_RISE;   // 上升沿采样
    i2s_config.div_number = 0;  // 自动计算分频
    
    // 初始化I2S（使用I2S_BUS_1用于播放，避免与采集冲突）
    errcode_t ret = uapi_i2s_init(I2S_BUS_1, &i2s_config);
    if (ret != ERRCODE_SUCC) {
        osal_printk("[AUDIO_PLAYBACK] Failed to init I2S: 0x%x\n", ret);
        return -1;
    }
    
    // 设置I2S发送模式
    ret = uapi_i2s_set_mode(I2S_BUS_1, I2S_MODE_TX);
    if (ret != ERRCODE_SUCC) {
        osal_printk("[AUDIO_PLAYBACK] Failed to set I2S TX mode: 0x%x\n", ret);
        uapi_i2s_deinit(I2S_BUS_1);
        return -1;
    }
    
    osal_printk("[AUDIO_PLAYBACK] I2S initialized: %d Hz, 16-bit, 2-ch\n", LITTLE_TEST_SAMPLE_RATE);
    return 0;
}

/**
 * @brief 初始化DMA LLI链表（播放）
 * @param playback 音频播放结构体指针
 * @return 0成功，其他失败
 */
static int audio_playback_init_dma_lli(audio_playback_t *playback)
{
    errcode_t ret;
    uint32_t i;
    
    // 分配DMA LLI节点
    playback->dma_lli_nodes = (dma_lli_node_t *)malloc(LITTLE_TEST_BUFFER_COUNT * sizeof(dma_lli_node_t));
    if (playback->dma_lli_nodes == NULL) {
        osal_printk("[AUDIO_PLAYBACK] Failed to allocate DMA LLI nodes\n");
        return -1;
    }
    
    // 配置每个DMA LLI节点
    for (i = 0; i < LITTLE_TEST_BUFFER_COUNT; i++) {
        dma_lli_node_t *node = &playback->dma_lli_nodes[i];
        
        // 配置DMA传输参数
        node->config.src_addr = (uintptr_t)playback->dma_buffers[i];  // 源缓冲区地址
        node->config.dest_addr = (uintptr_t)uapi_i2s_get_data_addr(I2S_BUS_1);  // I2S数据寄存器地址
        node->config.length = LITTLE_TEST_BUFFER_SIZE;  // 传输长度
        node->config.src_width = DMA_TRANSFER_WIDTH_16;  // 源数据宽度16位
        node->config.dest_width = DMA_TRANSFER_WIDTH_16;  // 目标数据宽度16位
        node->config.burst_length = DMA_BURST_LENGTH_4;  // 突发长度
        node->config.src_increment = DMA_ADDRESS_INC_YES;  // 源地址递增
        node->config.dest_increment = DMA_ADDRESS_INC_NO;  // 目标地址不递增（寄存器）
        node->config.priority = DMA_CHANNEL_PRIORITY_HIGH;  // 高优先级
        node->config.trans_type = DMA_TRANS_MEMORY_TO_PERIPHERAL_DMA;  // 内存到外设
        node->config.trans_dir = DMA_TRANSFER_DIR_MEM_TO_PERIPHERAL;  // 传输方向
        
        // 设置链表指针（循环链表）
        if (i == LITTLE_TEST_BUFFER_COUNT - 1) {
            node->next = &playback->dma_lli_nodes[0].config;  // 最后一个指向第一个
        } else {
            node->next = &playback->dma_lli_nodes[i + 1].config;  // 指向下一个
        }
    }
    
    // 申请DMA通道（使用不同的通道避免冲突）
    ret = uapi_dma_open();
    if (ret != ERRCODE_SUCC) {
        osal_printk("[AUDIO_PLAYBACK] Failed to open DMA: 0x%x\n", ret);
        free(playback->dma_lli_nodes);
        return -1;
    }
    
    // 配置DMA通道
    dma_channel_config_t dma_config = {0};
    dma_config.src_handshaking = DMA_HANDSHAKING_MAX;     // 内存不需要握手
    dma_config.dest_handshaking = DMA_HANDSHAKING_I2S_TX; // I2S发送握手
    dma_config.trans_type = DMA_TRANS_MEMORY_TO_PERIPHERAL_DMA;
    dma_config.trans_dir = DMA_TRANSFER_DIR_MEM_TO_PERIPHERAL;
    dma_config.priority = DMA_CHANNEL_PRIORITY_HIGH;
    
    ret = uapi_dma_configure_channel(playback->dma_channel, &dma_config);
    if (ret != ERRCODE_SUCC) {
        osal_printk("[AUDIO_PLAYBACK] Failed to configure DMA channel: 0x%x\n", ret);
        uapi_dma_close();
        free(playback->dma_lli_nodes);
        return -1;
    }
    
    // 注册DMA回调函数
    ret = uapi_dma_register_interrupt(playback->dma_channel, audio_playback_dma_callback, (uintptr_t)playback);
    if (ret != ERRCODE_SUCC) {
        osal_printk("[AUDIO_PLAYBACK] Failed to register DMA interrupt: 0x%x\n", ret);
        uapi_dma_close();
        free(playback->dma_lli_nodes);
        return -1;
    }
    
    osal_printk("[AUDIO_PLAYBACK] DMA LLI initialized: %d buffers, channel %d\n", 
                LITTLE_TEST_BUFFER_COUNT, playback->dma_channel);
    return 0;
}

/**
 * @brief 将单声道数据转换为双声道数据
 * @param mono_data 单声道数据
 * @param mono_samples 单声道样本数
 * @param stereo_data 双声道数据缓冲区
 */
static void convert_mono_to_stereo(const int16_t *mono_data, uint32_t mono_samples, int16_t *stereo_data)
{
    uint32_t i;
    
    for (i = 0; i < mono_samples; i++) {
        // 将单声道数据复制到左右声道
        stereo_data[i * 2] = mono_data[i];      // 左声道
        stereo_data[i * 2 + 1] = mono_data[i];  // 右声道
    }
}

/**
 * @brief 初始化音频播放模块
 * @param playback 音频播放结构体指针
 * @return 0成功，其他失败
 */
int audio_playback_init(audio_playback_t *playback)
{
    uint32_t i;
    errcode_t ret;
    
    if (playback == NULL) {
        osal_printk("[AUDIO_PLAYBACK] Invalid playback pointer\n");
        return -1;
    }

    // 初始化结构体
    memset_s(playback, sizeof(audio_playback_t), 0, sizeof(audio_playback_t));
    
    // 分配DMA缓冲区（双声道数据）
    for (i = 0; i < LITTLE_TEST_BUFFER_COUNT; i++) {
        playback->dma_buffers[i] = (uint8_t *)malloc(LITTLE_TEST_BUFFER_SIZE);
        if (playback->dma_buffers[i] == NULL) {
            osal_printk("[AUDIO_PLAYBACK] Failed to allocate DMA buffer %d\n", i);
            // 释放已分配的缓冲区
            for (uint32_t j = 0; j < i; j++) {
                free(playback->dma_buffers[j]);
            }
            return -1;
        }
        // 清零缓冲区
        memset_s(playback->dma_buffers[i], LITTLE_TEST_BUFFER_SIZE, 0, LITTLE_TEST_BUFFER_SIZE);
    }
    
    // 分配单声道到双声道转换缓冲区
    playback->conversion_buffer = (int16_t *)malloc(LITTLE_TEST_FRAME_SIZE * sizeof(int16_t));
    if (playback->conversion_buffer == NULL) {
        osal_printk("[AUDIO_PLAYBACK] Failed to allocate conversion buffer\n");
        for (i = 0; i < LITTLE_TEST_BUFFER_COUNT; i++) {
            free(playback->dma_buffers[i]);
        }
        return -1;
    }
    
    // 创建信号量
    ret = osal_sem_init(&playback->buffer_ready_sem, LITTLE_TEST_BUFFER_COUNT);
    if (ret != OSAL_SUCCESS) {
        osal_printk("[AUDIO_PLAYBACK] Failed to create semaphore\n");
        free(playback->conversion_buffer);
        for (i = 0; i < LITTLE_TEST_BUFFER_COUNT; i++) {
            free(playback->dma_buffers[i]);
        }
        return -1;
    }
    
    // 设置DMA通道（使用不同的通道，假设使用通道1）
    playback->dma_channel = 1;
    playback->current_buffer_index = 0;
    playback->frames_played = 0;
    playback->error_count = 0;
    playback->underrun_count = 0;
    
    // 初始化I2S
    if (audio_playback_init_i2s(playback) != 0) {
        osal_sem_destroy(&playback->buffer_ready_sem);
        free(playback->conversion_buffer);
        for (i = 0; i < LITTLE_TEST_BUFFER_COUNT; i++) {
            free(playback->dma_buffers[i]);
        }
        return -1;
    }
    
    // 初始化DMA LLI
    if (audio_playback_init_dma_lli(playback) != 0) {
        uapi_i2s_deinit(I2S_BUS_1);
        osal_sem_destroy(&playback->buffer_ready_sem);
        free(playback->conversion_buffer);
        for (i = 0; i < LITTLE_TEST_BUFFER_COUNT; i++) {
            free(playback->dma_buffers[i]);
        }
        return -1;
    }
    
    playback->initialized = true;
    playback->started = false;
    
    osal_printk("[AUDIO_PLAYBACK] Initialized successfully\n");
    osal_printk("[AUDIO_PLAYBACK] Buffer count: %d, Buffer size: %d bytes\n", 
                LITTLE_TEST_BUFFER_COUNT, LITTLE_TEST_BUFFER_SIZE);
    osal_printk("[AUDIO_PLAYBACK] Frame size: %d samples (%.1f ms)\n", 
                LITTLE_TEST_FRAME_SIZE, (float)LITTLE_TEST_FRAME_SIZE * 1000.0f / LITTLE_TEST_SAMPLE_RATE);
    
    return 0;
}

/**
 * @brief 反初始化音频播放模块
 * @param playback 音频播放结构体指针
 */
void audio_playback_deinit(audio_playback_t *playback)
{
    uint32_t i;
    
    if (playback == NULL) {
        return;
    }
    
    // 停止播放
    if (playback->started) {
        audio_playback_stop(playback);
    }
    
    // 反初始化DMA
    if (playback->dma_lli_nodes != NULL) {
        uapi_dma_unregister_interrupt(playback->dma_channel);
        uapi_dma_close();
        free(playback->dma_lli_nodes);
        playback->dma_lli_nodes = NULL;
    }
    
    // 反初始化I2S
    uapi_i2s_deinit(I2S_BUS_1);
    
    // 销毁信号量
    osal_sem_destroy(&playback->buffer_ready_sem);
    
    // 释放转换缓冲区
    if (playback->conversion_buffer != NULL) {
        free(playback->conversion_buffer);
        playback->conversion_buffer = NULL;
    }
    
    // 释放DMA缓冲区
    for (i = 0; i < LITTLE_TEST_BUFFER_COUNT; i++) {
        if (playback->dma_buffers[i] != NULL) {
            free(playback->dma_buffers[i]);
            playback->dma_buffers[i] = NULL;
        }
    }
    
    playback->initialized = false;
    
    osal_printk("[AUDIO_PLAYBACK] Deinitialized\n");
}

/**
 * @brief 开始音频播放
 * @param playback 音频播放结构体指针
 * @return 0成功，其他失败
 */
int audio_playback_start(audio_playback_t *playback)
{
    errcode_t ret;
    
    if (playback == NULL || !playback->initialized) {
        osal_printk("[AUDIO_PLAYBACK] Invalid playback or not initialized\n");
        return -1;
    }
    
    if (playback->started) {
        osal_printk("[AUDIO_PLAYBACK] Already started\n");
        return 0;
    }
    
    // 重置统计信息
    playback->current_buffer_index = 0;
    playback->frames_played = 0;
    playback->error_count = 0;
    playback->underrun_count = 0;
    
    // 启动DMA LLI传输
    ret = uapi_dma_start_lli_transfer(playback->dma_channel, 
                                     &playback->dma_lli_nodes[0].config, 
                                     DMA_LLI_TRANSFER_MODE_CIRCULAR);
    if (ret != ERRCODE_SUCC) {
        osal_printk("[AUDIO_PLAYBACK] Failed to start DMA LLI transfer: 0x%x\n", ret);
        return -1;
    }
    
    // 启动I2S发送
    ret = uapi_i2s_start(I2S_BUS_1);
    if (ret != ERRCODE_SUCC) {
        osal_printk("[AUDIO_PLAYBACK] Failed to start I2S: 0x%x\n", ret);
        uapi_dma_stop(playback->dma_channel);
        return -1;
    }
    
    playback->started = true;
    
    osal_printk("[AUDIO_PLAYBACK] Started successfully\n");
    return 0;
}

/**
 * @brief 停止音频播放
 * @param playback 音频播放结构体指针
 * @return 0成功，其他失败
 */
int audio_playback_stop(audio_playback_t *playback)
{
    if (playback == NULL || !playback->initialized) {
        return -1;
    }
    
    if (!playback->started) {
        return 0;
    }
    
    // 停止I2S
    uapi_i2s_stop(I2S_BUS_1);
    
    // 停止DMA
    uapi_dma_stop(playback->dma_channel);
    
    playback->started = false;
    
    osal_printk("[AUDIO_PLAYBACK] Stopped. Played %d frames, %d errors, %d underruns\n", 
                playback->frames_played, playback->error_count, playback->underrun_count);
    return 0;
}

/**
 * @brief 等待播放缓冲区就绪
 * @param playback 音频播放结构体指针
 * @param timeout_ms 超时时间（毫秒）
 * @return 0成功，其他失败
 */
int audio_playback_wait_buffer(audio_playback_t *playback, uint32_t timeout_ms)
{
    if (playback == NULL || !playback->initialized || !playback->started) {
        return -1;
    }
    
    // 等待信号量
    errcode_t ret = osal_sem_down_timeout(&playback->buffer_ready_sem, timeout_ms);
    if (ret != OSAL_SUCCESS) {
        if (ret == OSAL_FAILURE) {
            // 超时
            playback->underrun_count++;
            return -2;
        }
        return -1;
    }
    
    return 0;
}

/**
 * @brief 播放一帧音频数据
 * @param playback 音频播放结构体指针
 * @param frame 要播放的音频帧（单声道）
 * @return 0成功，其他失败
 */
int audio_playback_play_frame(audio_playback_t *playback, const audio_frame_t *frame)
{
    if (playback == NULL || !playback->initialized || frame == NULL) {
        return -1;
    }
    
    if (frame->size != LITTLE_TEST_FRAME_SIZE * sizeof(int16_t)) {
        osal_printk("[AUDIO_PLAYBACK] Invalid frame size: %d\n", frame->size);
        return -1;
    }
    
    // 获取当前可用的缓冲区
    uint32_t buffer_index = playback->current_buffer_index;
    int16_t *stereo_buffer = (int16_t *)playback->dma_buffers[buffer_index];
    const int16_t *mono_data = (const int16_t *)frame->data;
    
    // 将单声道数据转换为双声道数据
    convert_mono_to_stereo(mono_data, LITTLE_TEST_FRAME_SIZE, stereo_buffer);
    
    return 0;
}

/**
 * @brief 播放静音数据（用于填充缓冲区）
 * @param playback 音频播放结构体指针
 * @return 0成功，其他失败
 */
int audio_playback_play_silence(audio_playback_t *playback)
{
    if (playback == NULL || !playback->initialized) {
        return -1;
    }
    
    // 获取当前可用的缓冲区
    uint32_t buffer_index = playback->current_buffer_index;
    
    // 填充静音数据（全零）
    memset_s(playback->dma_buffers[buffer_index], LITTLE_TEST_BUFFER_SIZE, 0, LITTLE_TEST_BUFFER_SIZE);
    
    return 0;
}

/**
 * @brief 获取播放统计信息
 * @param playback 音频播放结构体指针
 * @param frames_played 输出已播放帧数
 * @param error_count 输出错误计数
 * @param underrun_count 输出欠载计数
 * @param current_buffer_index 输出当前缓冲区索引
 * @return 0成功，其他失败
 */
int audio_playback_get_stats(audio_playback_t *playback, 
                            uint32_t *frames_played, 
                            uint32_t *error_count, 
                            uint32_t *underrun_count,
                            uint32_t *current_buffer_index)
{
    if (playback == NULL || !playback->initialized) {
        return -1;
    }
    
    if (frames_played != NULL) {
        *frames_played = playback->frames_played;
    }
    
    if (error_count != NULL) {
        *error_count = playback->error_count;
    }
    
    if (underrun_count != NULL) {
        *underrun_count = playback->underrun_count;
    }
    
    if (current_buffer_index != NULL) {
        *current_buffer_index = playback->current_buffer_index;
    }
    
    return 0;
}